﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using Common.Utilities;
using Common.Utilities.Dispatcher;

namespace Workflows.Components.Rules
{
    /// <summary>
    /// bind to business object
    /// </summary>
    /// <param name="rule"></param>
    public delegate void RuleUpdated(Rule rule);

    public partial class RuleEditor : Form
    {
        public event RuleUpdated OnRuleUpdated;
        private Rule _Rule;
        /// <summary>
        /// 
        /// </summary>
        public RuleEditor()
        {
            InitializeComponent();
        }

        private void RuleEditor_Load(object sender, EventArgs e)
        {
            this.cbo_Assemblies.Items.Clear();
            string binFolderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            DirectoryInfo dirInfo=new DirectoryInfo(binFolderPath);
            FileInfo[] dllinfos = dirInfo.GetFiles("*.dll", SearchOption.TopDirectoryOnly);
            if(dllinfos !=null && dllinfos.Length>0)
            {
                foreach(FileInfo fileInfo in dllinfos)
                {
                    this.cbo_Assemblies.Items.Add(fileInfo.Name);
                }
                this.cbo_Assemblies.SelectedIndex = 0;
            }

            foreach (DataGridViewRow dr in this.dv_Vars.Rows)
            {
                if (dr.IsNewRow)
                {
                    DataGridViewComboBoxCell cellDataType = (DataGridViewComboBoxCell)dr.Cells[1];
                    cellDataType.Items.Clear();
                    for (int i = 0; i <= 5; i++)
                    {
                        SimpleDataType dataType = (SimpleDataType)i;
                        cellDataType.Items.Add(dataType.ToString());
                    }
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        public Rule Rule
        {
            get
            {
                this.SaveSettings();
                return this._Rule;
            }
            set
            {
                this._Rule = value;
                this.DisplayRuleSettings();
            }
        }

        private void DisplayRuleSettings()
        {
            this.txt_RuleName.Text = this._Rule.RuleName;
            this.txt_Category.Text = this._Rule.RuleCategory;
            this.txt_Desc.Text = this._Rule.Description;

            if(this.cbo_Assemblies.Items.Count>0 && !string.IsNullOrEmpty(this._Rule.AssemblyFilePath))
            {
                string assemblyFileName = Path.GetFileName(this._Rule.AssemblyFilePath);
                for(int i=0;i<this.cbo_Assemblies.Items.Count;i++)
                {
                    if(this.cbo_Assemblies.Items[i].ToString()==assemblyFileName)
                    {
                        this.cbo_Assemblies.SelectedIndex = i;
                        break;
                    }
                }
            }

            if (this.cbo_Objs.Items.Count > 0 && (!string.IsNullOrEmpty(this._Rule.BusinessObjectName)))
            {
                for(int i=0;i<this.cbo_Objs.Items.Count;i++)
                {
                    if(this.cbo_Objs.Items[i].ToString()==this._Rule.BusinessObjectName)
                    {
                        this.cbo_Objs.SelectedIndex = i;
                        break;
                    }
                }
            }

            this.dv_Vars.Rows.Clear();
            if(this._Rule.Vars !=null && this._Rule.Vars.Length>0)
            {
                foreach(GlobalVariable var in this._Rule.Vars)
                {
                    DataGridViewRow dr=new DataGridViewRow();
                    DataGridViewTextBoxCell cellVarName=new DataGridViewTextBoxCell();
                    cellVarName.Value = var.VarName;
                    dr.Cells.Add(cellVarName);
                    DataGridViewComboBoxCell cellDataType=new DataGridViewComboBoxCell();
                    for(int i=0;i<=5;i++)
                    {
                        SimpleDataType dataType = (SimpleDataType) i;
                        cellDataType.Items.Add(dataType.ToString());
                    }
                    cellDataType.Value = var.DataType.ToString();
                    dr.Cells.Add(cellDataType);
                    DataGridViewCheckBoxCell cellReturn=new DataGridViewCheckBoxCell();
                    dr.Cells.Add(cellReturn);

                    this.dv_Vars.Rows.Add(dr);
                }
            }

            foreach (DataGridViewRow dr in this.dv_Vars.Rows)
            {
                if (dr.IsNewRow)
                {
                    DataGridViewComboBoxCell cellDataType = (DataGridViewComboBoxCell)dr.Cells[1];
                    cellDataType.Items.Clear();
                    for (int i = 0; i <= 5; i++)
                    {
                        SimpleDataType dataType = (SimpleDataType)i;
                        cellDataType.Items.Add(dataType.ToString());
                    }
                }
            }
        }

        private bool SaveSettings()
        {
            if(this.cbo_Assemblies.SelectedIndex<0)
            {
                MessageBox.Show("Please select assembly");
                return false;
            }
            if(this.cbo_Objs.SelectedIndex<0)
            {
                MessageBox.Show("Please select bindable object");
                return false;
            }
            this._Rule.RuleName = this.txt_RuleName.Text;
            this._Rule.RuleCategory = this.txt_Category.Text;
            this._Rule.Description = this.txt_Desc.Text;
            if (this.cbo_Assemblies.SelectedIndex >= 0)
            {
                string assemblyFileName = this.cbo_Assemblies.SelectedItem.ToString();
                string binFolderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                this._Rule.AssemblyFilePath = Path.Combine(binFolderPath, assemblyFileName);
            }
            else if((!string.IsNullOrEmpty(this.cbo_Assemblies.Text)) && File.Exists(this.cbo_Assemblies.Text))
            {
                this._Rule.AssemblyFilePath = this.cbo_Assemblies.Text;
            }
            if (this.cbo_Objs.SelectedIndex >= 0)
            {
                this._Rule.BusinessObjectName = this.cbo_Objs.SelectedItem.ToString();
            }
            else
            {
                this._Rule.BusinessObjectName = this.cbo_Objs.Text;
            }
            if ((!string.IsNullOrEmpty(this._Rule.AssemblyFilePath)) && (!string.IsNullOrEmpty(this._Rule.BusinessObjectName)))
            {
                Assembly assem = Assembly.LoadFile(this._Rule.AssemblyFilePath);
                Type boType = assem.GetType(this._Rule.BusinessObjectName);

                if (boType != null)
                {
                    object businessObj = null;
                    Type[] args = new Type[0];
                    ConstructorInfo ctor = boType.GetConstructor(args);
                    if (ctor != null)
                    {
                        businessObj = ctor.Invoke(null);
                    }
                    if (businessObj == null)
                    {
                        MessageBox.Show("Bindable object must have an empty constructor");
                        return false;
                    }
                }
            }

            this._Rule.Vars = null;
            List<GlobalVariable> varList=new List<GlobalVariable>();
            foreach(DataGridViewRow dr in this.dv_Vars.Rows)
            {
                if(!dr.IsNewRow)
                {
                    DataGridViewTextBoxCell cellVarName = (DataGridViewTextBoxCell) dr.Cells[0];
                    string varName = cellVarName.Value.ToString();
                    DataGridViewComboBoxCell cellDataType = (DataGridViewComboBoxCell) dr.Cells[1];
                    SimpleDataType dataType =(SimpleDataType) Enum.Parse(typeof (SimpleDataType), cellDataType.Value.ToString());
                    GlobalVariable var = new GlobalVariable(varName, dataType, null);
                    varList.Add(var);
                }
            }
            if(varList.Count>0)
            {
                this._Rule.Vars = varList.ToArray();
            }
            return true;
        }

        private void cbo_Assemblies_SelectedIndexChanged(object sender, EventArgs e)
        {
            if(this.cbo_Assemblies.SelectedIndex>=0)
            {
                string dllFileName = this.cbo_Assemblies.SelectedItem.ToString();
                string binFolderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                string dllFilePath = Path.Combine(binFolderPath, dllFileName);
                Assembly assem = Assembly.LoadFile(dllFilePath);
                Type[] types = assem.GetTypes();
                this.cbo_Objs.Items.Clear();
                List<string> typeNames=new List<string>();
                foreach(Type type in types)
                {
                    if(type.IsClass && (!type.IsAbstract))
                    {
                        typeNames.Add(type.FullName);
                    }
                }
                if(typeNames.Count>0)
                {
                    typeNames.Sort();
                    foreach(string typeName in typeNames)
                    {
                        this.cbo_Objs.Items.Add(typeName);
                    }
                    this.cbo_Objs.SelectedIndex = 0;
                }
            }
        }

        private void btn_OK_Click(object sender, EventArgs e)
        {
            if(this.SaveSettings())
            {
                if(this.OnRuleUpdated !=null)
                {
                    this.OnRuleUpdated(this._Rule);
                }
                this.Close();
            }
        }

        private void btn_Cancel_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void dv_Vars_RowValidating(object sender, DataGridViewCellCancelEventArgs e)
        {
            foreach(DataGridViewRow dr in this.dv_Vars.Rows)
            {
                if(dr.IsNewRow)
                {
                    DataGridViewComboBoxCell cellDataType = (DataGridViewComboBoxCell) dr.Cells[1];
                    cellDataType.Items.Clear();
                    for(int i=0;i<=5;i++)
                    {
                        SimpleDataType dataType = (SimpleDataType) i;
                        cellDataType.Items.Add(dataType.ToString());
                    }
                }
            }
        }

        private void btn_BrowseAssembly_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlg=new OpenFileDialog();
            dlg.Filter = "Assemblies(*.dll)|*.dll|All files(*.*)|*.*";
            if(dlg.ShowDialog()==DialogResult.OK)
            {
                this.cbo_Assemblies.Text = dlg.FileName;
            }
        }

        private void cbo_Objs_SelectedIndexChanged(object sender, EventArgs e)
        {
            string assemblyFilePath = string.Empty;
            if (this.cbo_Assemblies.SelectedIndex >= 0 && this.cbo_Objs.SelectedIndex >= 0)
            {
                string assemblyFileName = this.cbo_Assemblies.SelectedItem.ToString();
                string appFolderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                assemblyFilePath = Path.Combine(appFolderPath, assemblyFileName);
            }
            else if(!string.IsNullOrEmpty(this.cbo_Assemblies.Text))
            {
                if(File.Exists(this.cbo_Assemblies.Text))
                {
                    assemblyFilePath = this.cbo_Assemblies.Text;
                }
            }

            if((!string.IsNullOrEmpty(assemblyFilePath)) && this.cbo_Objs.SelectedIndex>=0)
            {
                Assembly assem = Assembly.LoadFile(assemblyFilePath);
                Type type = assem.GetType(this.cbo_Objs.SelectedItem.ToString());
                Dictionary<string, GlobalVariable> globalVars=new Dictionary<string, GlobalVariable>();
                FieldInfo[] fields = type.GetFields();
                if(fields !=null && fields.Length>0)
                {
                    foreach(FieldInfo field in fields)
                    {
                        if(field.IsPublic)
                        {
                            if(!globalVars.ContainsKey(field.Name))
                            {
                                globalVars.Add(field.Name,
                                               new GlobalVariable(field.Name,
                                                                  SimpleDataTypeUtil.ToSimpleDataType(field.FieldType),
                                                                  null));
                            }
                        }
                    }
                }
                PropertyInfo[] props = type.GetProperties();
                if(props !=null && props.Length>0)
                {
                    foreach(PropertyInfo prop in props)
                    {
                        if(!globalVars.ContainsKey(prop.Name))
                        {
                            globalVars.Add(prop.Name,
                                           new GlobalVariable(prop.Name,
                                                              SimpleDataTypeUtil.ToSimpleDataType(prop.PropertyType),
                                                              null));   
                        }
                    }
                }

                if(globalVars.Count>0)
                {
                    foreach(string gVarName in globalVars.Keys)
                    {
                        bool exist = false;
                        foreach(DataGridViewRow dr in this.dv_Vars.Rows)
                        {
                            if(!dr.IsNewRow && dr.Cells[0].Value !=null)
                            {
                                string varName = dr.Cells[0].Value.ToString();
                                if(varName==gVarName)
                                {
                                    exist = true;
                                    break;
                                }
                            }
                        }
                        if(!exist)
                        {
                            DataGridViewRow dr=new DataGridViewRow();
                            DataGridViewTextBoxCell cellVarName=new DataGridViewTextBoxCell();
                            cellVarName.Value = gVarName;
                            dr.Cells.Add(cellVarName);
                            DataGridViewComboBoxCell cellDataType = new DataGridViewComboBoxCell();
                            for (int i = 0; i <= 5; i++)
                            {
                                SimpleDataType dataType = (SimpleDataType)i;
                                cellDataType.Items.Add(dataType.ToString());
                            }
                            cellDataType.Value = globalVars[gVarName].DataType.ToString();
                            dr.Cells.Add(cellDataType);
                            DataGridViewCheckBoxCell cellReturn = new DataGridViewCheckBoxCell();
                            dr.Cells.Add(cellReturn);
                            this.dv_Vars.Rows.Add(dr);
                        }
                    }
                }
            }
        }
    }
}
